home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / sound / record sound to disk / record_sound.c < prev   
Encoding:
C/C++ Source or Header  |  2000-09-28  |  16.9 KB  |  551 lines

  1. /*
  2.     File:        Record_sound.c
  3.  
  4.     Contains:    This sample shows how to use SPBRecord to record to memory and then
  5.                 write the recorded samples to disk, asynchronously, using PBWriteAsync.
  6.  
  7.                 This sample is useful for those developers who wish more flexibility
  8.                 than what is offered with SPBRecordToFile.
  9.  
  10.                 This sample uses two parameter blocks because as Macs get faster, and
  11.                 drives can't quite keep up, using just one parameter block for two writes
  12.                 is just asking for trouble.  It's not that much extra memory and it will
  13.                 be a weird bug you never have to find.  The pbInUse field of each parameter
  14.                 block is supposed to keep us from reusing the parameter block in case things
  15.                 get really stopped up.
  16.  
  17.                 These routines are designed to be easliy portable (in fact they were ripped
  18.                 from another sample), so you should be able to use them with little modification.
  19.  
  20.                 It checks for errors returned from SPBRecord and from the PBWrites.  If there
  21.                 is a disk error returned from a PBWrite the SPBRecord completion routine will
  22.                 not start the next record, killing the recording process and the error will be
  23.                 returned in the Vars structure (in the theErr field).
  24.  
  25.                 Happy recording!
  26.  
  27.     Written by: Mark Cookson    
  28.  
  29.     Copyright:    Copyright Â© 1996-1999 by Apple Computer, Inc., All Rights Reserved.
  30.  
  31.                 You may incorporate this Apple sample source code into your program(s) without
  32.                 restriction. This Apple sample source code has been provided "AS IS" and the
  33.                 responsibility for its operation is yours. You are not permitted to redistribute
  34.                 this Apple sample source code as "Apple sample source code" after having made
  35.                 changes. If you're going to re-distribute the source, we require that you make
  36.                 it clear in the source that the code was descended from Apple sample source
  37.                 code, but that you've made changes.
  38.  
  39.     Change History (most recent first):
  40.                 8/2/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  41.                 
  42.  
  43. */
  44.  
  45. #include        <Quickdraw.h>
  46. #include        <Windows.h>
  47. #include        <dialogs.h>
  48. #include        <OSEvents.h>
  49. #include        <Memory.h>
  50. #include        <Packages.h>
  51. #include        <Sound.h>
  52. #include        <SoundInput.h>
  53. #include        <OSUtils.h>
  54. #include        <Files.h>
  55. #include        <StandardFile.h>
  56. #include        <Fonts.h>
  57. #include        <Devices.h>
  58.  
  59. //So we can pass our A5 and other info to our PBWrite completion routines
  60. typedef struct myParamBlockRec {
  61.     ParamBlockRec            pb;
  62.     long                    myA5;
  63.     Boolean                    pbInUse;
  64. }myParamBlockRec, *myParmBlkPtr;
  65.  
  66. //Keep track of the info needed to record
  67. typedef struct {
  68.         long            sanitycheck;
  69.         SPBPtr            recordRec;
  70.         myParmBlkPtr    pb0,
  71.                         pb1;
  72.         Ptr                recBuffer0,
  73.                         recBuffer1;
  74.         Fixed            sampleRate;
  75.         OSType            compression;
  76.         unsigned long    totalBytes;
  77.         long            myA5,
  78.                         devBuffer,
  79.                         soundRefNum;
  80.         short            whichBuffer,
  81.                         fileRefNum,
  82.                         numChannels,
  83.                         sampleSize;
  84.         OSErr            theErr;        //last error returned by SPBRecord or PBWrite
  85. } Vars, *VarsPtr;
  86.  
  87. OSErr            PrepairToRecordToDisk    (VarsPtr myVars);
  88. OSErr            RecordToDisk             (VarsPtr myVars);
  89. pascal void        MyRecComp                (SPBPtr inParamPtr);
  90. OSErr            FinishRecording         (VarsPtr myVars);
  91.  
  92. #if GENERATINGCFM
  93. pascal void        MyPB0WriteComp            (myParmBlkPtr passedPB);
  94. pascal void        MyPB1WriteComp            (myParmBlkPtr passedPB);
  95. #else 
  96. pascal void        MyPB0WriteComp            (myParmBlkPtr passedPB:__a0);
  97. pascal void        MyPB1WriteComp            (myParmBlkPtr passedPB:__a0);
  98. #endif
  99.  
  100. static void ToolBoxInit (void)
  101. {
  102.     MaxApplZone();
  103.     InitGraf (&qd.thePort);
  104.     InitFonts ();
  105.     InitWindows ();
  106.     InitMenus ();
  107.     TEInit ();
  108.     InitDialogs ((long)nil);
  109.     InitCursor ();
  110.     return;
  111. }
  112.  
  113. void main (void) {
  114.     Vars        myVars;
  115.     Rect        r        = {100, 90, 150, 400};
  116.     WindowPtr    window    = nil;
  117.     unsigned long    temp;
  118.     OSErr        err;
  119.  
  120.     ToolBoxInit ();
  121.  
  122.     err = PrepairToRecordToDisk (&myVars);
  123.     err = RecordToDisk (&myVars);
  124.  
  125.     //Make a little window
  126.     window = NewWindow (nil, &r, "\pRecording Sound", true, 0, (WindowRef)-1, false, 0);
  127.     SetPort (window);
  128.     MoveTo (20,20);
  129.     DrawString ("\pRecording 3 seconds of sound…");
  130.  
  131.     Delay (180, &temp);        //record for about three seconds
  132.  
  133.     err = myVars.theErr;    //were any errors returned from the recording?
  134.     if (err != noErr && err != abortErr) {
  135.         DebugStr ("\pRecording didn't complete without error");
  136.     }
  137.  
  138.     DisposeWindow (window);
  139.  
  140.     //if the error was that we ran out of disk space we can still write the
  141.     //header because we preallocated space for it.  The file may be bad,
  142.     //but it doesn't have to be.  At any rate we want to close the file and
  143.     //dispose of memory.
  144.     err = FinishRecording (&myVars);
  145. }
  146.  
  147. static OSErr    SetupDevice (long inputDevice,
  148.                             short *numChannels,
  149.                             short *sampleSize,
  150.                             Fixed *sampleRate,
  151.                             OSType *compression,
  152.                             long *devBuffer) {
  153.  
  154.     OSErr                err;
  155.     Fixed                gain = 0x00008000;
  156.     short                on = 1;
  157.  
  158.     err = SPBSetDeviceInfo (inputDevice, siSampleRate, (Ptr) sampleRate);
  159.     if (err != noErr)
  160.         DebugStr("\pcouldn't set sample rate");
  161.  
  162.     err = SPBSetDeviceInfo (inputDevice, siSampleSize, (Ptr) sampleSize);
  163.     if (err != noErr)
  164.         DebugStr("\pcouldn't set sample size");
  165.  
  166.     err = SPBSetDeviceInfo (inputDevice, siTwosComplementOnOff, (Ptr) &on);
  167.     if (err != noErr)
  168.         DebugStr("\pcouldn't set twos complement");
  169.  
  170.     err = SPBSetDeviceInfo (inputDevice, siNumberChannels, (Ptr) numChannels);
  171.     if (err != noErr)
  172.         DebugStr("\pcouldn't set number of channels");
  173.  
  174.     err = SPBSetDeviceInfo (inputDevice, siCompressionType, (Ptr) compression);
  175.     if (err != noErr)
  176.         DebugStr("\pcouldn't set compression type");
  177.  
  178.     //turn on continuous recording to "warm up" the input device
  179.     err = SPBSetDeviceInfo (inputDevice, siContinuous, (Ptr) &on);
  180.     if (err != noErr)
  181.         DebugStr("\pcouldn't turn on continuous recording");
  182.  
  183.     //turn on Automatic Gain Control
  184.     err = SPBSetDeviceInfo (inputDevice, siAGCOnOff, (Ptr) &on);
  185.     if (err != noErr) {
  186.         //If AGC isn't available, just turn it all the way down to avoid over driving
  187.         err = SPBSetDeviceInfo (inputDevice, siInputGain, (Ptr) &gain);
  188.         if (err != noErr)
  189.             DebugStr("\pcouldn't get siInputGain");
  190.     }
  191.  
  192.     //check to see what we really got
  193.     err = SPBGetDeviceInfo (inputDevice, siSampleRate, (Ptr) sampleRate);
  194.     if (err != noErr)
  195.         DebugStr("\pcouldn't get sample rate");
  196.  
  197.     err = SPBGetDeviceInfo (inputDevice, siSampleSize, (Ptr) sampleSize);
  198.     if (err != noErr)
  199.         DebugStr("\pcouldn't get sample size");
  200.  
  201.     err = SPBGetDeviceInfo (inputDevice, siNumberChannels, (Ptr) numChannels);
  202.     if (err != noErr)
  203.         DebugStr("\pcouldn't get number of channels");
  204.  
  205.     err = SPBGetDeviceInfo (inputDevice, siDeviceBufferInfo, (Ptr) devBuffer);
  206.     if (err != noErr)
  207.         DebugStr("\pcouldn't get number of channels");
  208.  
  209.     err = SPBGetDeviceInfo (inputDevice, siCompressionType, (Ptr) compression);
  210.     if (err != noErr)
  211.         DebugStr("\pcouldn't get compression type");
  212.  
  213.     return err;
  214. }
  215.  
  216. OSErr    PrepairToRecordToDisk (VarsPtr myVars) {
  217.     OSErr                err;
  218.     short                volRefNum;
  219.     long                buffSize, parID;
  220.     StandardFileReply    sfReply;
  221.     StringPtr            nameString;
  222.  
  223.     StandardPutFile ("\pPut recorded AIFF where...", "\pRecorded sound", &sfReply);
  224.     if (sfReply.sfGood) {
  225.         volRefNum = sfReply.sfFile.vRefNum;
  226.         parID = sfReply.sfFile.parID;
  227.         nameString = (StringPtr)&sfReply.sfFile.name;
  228.  
  229.         err = SPBOpenDevice (nil, siWritePermission, &myVars->soundRefNum);
  230.         if (err != noErr) {
  231.             DebugStr("\pcouldn't open the device");
  232.         }
  233.  
  234.         myVars->numChannels    = 2;
  235.         myVars->sampleSize    = 16;
  236.         myVars->sampleRate    = rate44khz;
  237.         myVars->compression    = 'NONE';
  238.         err = SetupDevice (myVars->soundRefNum, &myVars->numChannels,
  239.                                                 &myVars->sampleSize,
  240.                                                 &myVars->sampleRate,
  241.                                                 &myVars->compression,
  242.                                                 &myVars->devBuffer);
  243.  
  244.         buffSize = myVars->devBuffer * 20;    //make our buffer a multiple of the hardware's
  245.         myVars->recBuffer0 = NewPtrClear(buffSize);
  246.         if (MemError() != noErr || myVars->recBuffer0 == nil) {
  247.             DebugStr("\pcouldn't get memory for recording buffer");
  248.             err = memFullErr;
  249.         }
  250.  
  251.         myVars->recBuffer1 = NewPtrClear(buffSize);
  252.         if (MemError() != noErr || myVars->recBuffer1 == nil) {
  253.             DebugStr("\pcouldn't get memory for recording buffer");
  254.             err = memFullErr;
  255.         }
  256.  
  257.         if (err == noErr) {
  258.             myVars->recordRec = (SPBPtr) NewPtrClear(sizeof (SPB));
  259.             if (MemError() != noErr || myVars->recordRec == nil) {
  260.                 DebugStr("\pcouldn't get memory for recording record");
  261.                 err = memFullErr;
  262.             }
  263.         }
  264.  
  265.         if (err == noErr) {
  266.             myVars->pb0 = (myParmBlkPtr) NewPtrClear(sizeof (myParamBlockRec));
  267.             if (MemError() != noErr || myVars->pb0 == nil) {
  268.                 DebugStr("\pcouldn't get memory for param block");
  269.                 err = memFullErr;
  270.             }
  271.         }
  272.  
  273.         if (err == noErr) {
  274.             myVars->pb1 = (myParmBlkPtr) NewPtrClear(sizeof (myParamBlockRec));
  275.             if (MemError() != noErr || myVars->pb1 == nil) {
  276.                 DebugStr("\pcouldn't get memory for param block");
  277.                 err = memFullErr;
  278.             }
  279.         }
  280.  
  281.         if (err == noErr) {
  282.             HParamBlockRec        Hpb;
  283.             IOCompletionUPP     MyPB0WriteCompUPP,
  284.                                 MyPB1WriteCompUPP;
  285.             SICompletionUPP     MyRecCompUPP;
  286.  
  287.             myVars->sanitycheck = 'SANE';
  288.             myVars->myA5 = SetCurrentA5 ();
  289.             myVars->whichBuffer = 0;
  290.             myVars->pb0->myA5 = SetCurrentA5 ();
  291.             myVars->pb1->myA5 = SetCurrentA5 ();
  292.  
  293.             //set up the record parameters
  294.             MyRecCompUPP = NewSICompletionProc (MyRecComp);
  295.             myVars->recordRec->inRefNum = myVars->soundRefNum;
  296.             myVars->recordRec->count = buffSize;
  297.             myVars->recordRec->milliseconds = 0;
  298.             myVars->recordRec->bufferLength = buffSize;
  299.             myVars->recordRec->bufferPtr = myVars->recBuffer0;
  300.             myVars->recordRec->completionRoutine = MyRecCompUPP;
  301.             myVars->recordRec->interruptRoutine = nil;
  302.             myVars->recordRec->userLong = (long)myVars;
  303.             myVars->recordRec->error = 0;
  304.             myVars->recordRec->unused1 = 0;
  305.  
  306.             //create the file
  307.             Hpb.ioParam.ioCompletion = nil;
  308.             Hpb.ioParam.ioNamePtr = nameString;
  309.             Hpb.ioParam.ioVRefNum = volRefNum;
  310.             Hpb.fileParam.ioDirID = parID;
  311.             err = PBHCreateSync (&Hpb);
  312.             if (err == dupFNErr) {
  313.                 err = noErr;    //overwriting the file is not an error
  314.             }
  315.             if (err != noErr)
  316.                 DebugStr("\pPBHCreateSync failed");
  317.  
  318.             if (err == noErr) {
  319.                 //set the file type and creator
  320.                 Hpb.fileParam.ioVRefNum = volRefNum;
  321.                 Hpb.fileParam.ioDirID = parID;
  322.                 Hpb.fileParam.ioNamePtr = nameString;
  323.                 Hpb.fileParam.ioFVersNum = 0;
  324.                 Hpb.fileParam.ioFDirIndex = 0;
  325.                 err = PBHGetFInfoSync(&Hpb);
  326.                 if (err != noErr)
  327.                     DebugStr("\pPBHGetFInfoSync failed");
  328.             }
  329.             if (err == noErr) {
  330.                 Hpb.fileParam.ioFlFndrInfo.fdType = 'AIFF';
  331.                 Hpb.fileParam.ioFlFndrInfo.fdCreator = 'myAp';
  332.                 Hpb.fileParam.ioDirID = parID;
  333.                 err = PBHSetFInfoSync (&Hpb);
  334.                 if (err != noErr)
  335.                     DebugStr("\pPBHSetFInfoSync failed");
  336.             }
  337.  
  338.             if (err == noErr) {
  339.                 //open the file for writing
  340.                 Hpb.ioParam.ioCompletion = nil;
  341.                 Hpb.ioParam.ioNamePtr = nameString;
  342.                 Hpb.ioParam.ioVRefNum = volRefNum;
  343.                 Hpb.ioParam.ioPermssn = fsRdWrPerm;
  344.                 Hpb.fileParam.ioDirID = parID;
  345.                 err = PBHOpenDFSync (&Hpb);
  346.                 if (err != noErr)
  347.                     DebugStr("\pPBHOpenDFSync failed");
  348.             }
  349.  
  350.             if (err == noErr) {
  351.                 myVars->fileRefNum = Hpb.ioParam.ioRefNum;
  352.  
  353.                 //set up the parameter blocks for the coming writes
  354.                 MyPB0WriteCompUPP = NewIOCompletionProc (MyPB0WriteComp);
  355.                 MyPB1WriteCompUPP = NewIOCompletionProc (MyPB1WriteComp);
  356.                 myVars->pb0->pb.ioParam.ioCompletion = MyPB0WriteCompUPP;
  357.                 myVars->pb0->pb.ioParam.ioVRefNum = volRefNum;
  358.                 myVars->pb0->pb.ioParam.ioRefNum = myVars->fileRefNum;
  359.                 myVars->pb0->pb.ioParam.ioPosMode = fsAtMark;
  360.  
  361.                 myVars->pb1->pb.ioParam.ioCompletion = MyPB1WriteCompUPP;
  362.                 myVars->pb1->pb.ioParam.ioVRefNum = volRefNum;
  363.                 myVars->pb1->pb.ioParam.ioRefNum = myVars->fileRefNum;
  364.                 myVars->pb1->pb.ioParam.ioPosMode = fsAtMark;
  365.  
  366.                 //write a temp AIFF header so file pointer for data is in the right place
  367.                 err = SetupAIFFHeader (myVars->fileRefNum, myVars->numChannels, myVars->sampleRate, myVars->sampleSize, myVars->compression, 0, 0);
  368.                 if (err != noErr)
  369.                     DebugStr("\pSetupAIFFHeader failed");
  370.             }
  371.         }
  372.     } else {
  373.         err = userCanceledErr;
  374.     }
  375.  
  376.     return err;
  377. }
  378.  
  379. OSErr RecordToDisk (VarsPtr myVars) {
  380.     OSErr            err = noErr;
  381.  
  382.     myVars->totalBytes = 0;
  383.     err = SPBRecord (myVars->recordRec, true);
  384.     if (err != noErr) {
  385.         DebugStr("\pSPBRecord failed");
  386.     }
  387.  
  388.     return err;
  389. }
  390.  
  391. /*
  392.     Stops the recording by calling SPBStopRecording and then writes the
  393.     correct file header to the file, closes the file and then disposes of
  394.     our pointers.
  395. */
  396. OSErr    FinishRecording (VarsPtr myVars) {
  397.     OSErr            err;
  398.     ParamBlockRec    pb;
  399.  
  400.     err = SPBStopRecording (myVars->soundRefNum);
  401.     if (err != noErr)
  402.         DebugStr("\pSPBStopRecording failed");
  403.  
  404.     err = SPBCloseDevice (myVars->soundRefNum);
  405.     if (err != noErr)
  406.         DebugStr("\pSPBCloseDevice failed");
  407.  
  408.     //Put the file pointer back to the start of the file to
  409.     //write the correct header over the temp header
  410.     pb.ioParam.ioCompletion = nil;
  411.     pb.ioParam.ioRefNum = myVars->fileRefNum;
  412.     pb.ioParam.ioPosMode = fsFromStart;
  413.     pb.ioParam.ioPosOffset = 0;
  414.     err = PBSetFPosSync (&pb);
  415.     if (err != noErr)
  416.         DebugStr("\pPBSetFPosSync failed");
  417.  
  418.     if (err == noErr) {
  419.         //write the header with the correct information
  420.         err = SetupAIFFHeader (myVars->fileRefNum, myVars->numChannels, myVars->sampleRate, myVars->sampleSize, myVars->compression, myVars->totalBytes, myVars->totalBytes/(myVars->sampleSize*myVars->numChannels));
  421.         if (err != noErr)
  422.             DebugStr("\pSetupAIFFHeader in FinishRecording failed");
  423.     }
  424.  
  425.     err = PBCloseSync (&pb);
  426.     if (err != noErr)
  427.         DebugStr("\pPBCloseSync failed");
  428.  
  429.     DisposePtr ((Ptr)myVars->recordRec);
  430.     DisposePtr (myVars->recBuffer0);
  431.     DisposePtr (myVars->recBuffer1);
  432.     DisposePtr ((Ptr)myVars->pb0);
  433.     DisposePtr ((Ptr)myVars->pb1);
  434.  
  435.     return err;
  436. }
  437.  
  438. /*
  439.     This gets called at the end of each record, it writes the recorded data to
  440.     disk (asynchronously) and then starts a new record using the same SPBPtr.
  441.  
  442.     Make sure you have continous recording turned on otherwise the recording
  443.     may loose data between calls to SPBRecord.    
  444. */
  445. pascal void MyRecComp (SPBPtr inParamPtr) {
  446.     VarsPtr            myVarsPtr;
  447.     OSErr            err;
  448.  
  449.     #if !GENERATINGCFM
  450.         long        oldA5;
  451.         oldA5 = SetA5 (((VarsPtr)inParamPtr->userLong)->myA5);
  452.     #endif
  453.  
  454.     myVarsPtr = (VarsPtr)inParamPtr->userLong;
  455.  
  456.     //setting this now will avoid a race condition of the record finishing before
  457.     //we can toggle which buffer to use
  458.     myVarsPtr->whichBuffer = !myVarsPtr->whichBuffer;
  459.  
  460.     //If the last write returned with no error then continue recording
  461.     if (myVarsPtr->pb0->pb.ioParam.ioResult == noErr && myVarsPtr->pb1->pb.ioParam.ioResult == noErr) {
  462.         //If we are aborting (stopping) the recording, we still want to write the last buffer
  463.         if (inParamPtr->error == noErr || inParamPtr->error == abortErr) {
  464.             if (myVarsPtr->whichBuffer == 1) {
  465.                 myVarsPtr->pb0->pb.ioParam.ioBuffer = myVarsPtr->recBuffer0;
  466.                 myVarsPtr->pb0->pb.ioParam.ioReqCount = myVarsPtr->recordRec->count;
  467.                 myVarsPtr->totalBytes += myVarsPtr->recordRec->count;
  468.                 if (myVarsPtr->pb0->pbInUse == false) {
  469.                     myVarsPtr->pb0->pbInUse = true;
  470.                     err = PBWriteAsync (&myVarsPtr->pb0->pb);
  471.                 }
  472.                 inParamPtr->bufferPtr = myVarsPtr->recBuffer1;
  473.                 if (inParamPtr->error == noErr) {
  474.                     err = SPBRecord (inParamPtr, true);
  475.                     if (err != noErr)
  476.                         DebugStr("\pSPBRecord1 failed");
  477.                 }
  478.             } else {
  479.                 myVarsPtr->pb1->pb.ioParam.ioBuffer = myVarsPtr->recBuffer1;
  480.                 myVarsPtr->pb1->pb.ioParam.ioReqCount = myVarsPtr->recordRec->count;
  481.                 myVarsPtr->totalBytes += myVarsPtr->recordRec->count;
  482.                 if (myVarsPtr->pb1->pbInUse == false) {
  483.                     myVarsPtr->pb1->pbInUse = true;
  484.                     err = PBWriteAsync (&myVarsPtr->pb1->pb);
  485.                 }
  486.                 inParamPtr->bufferPtr = myVarsPtr->recBuffer0;
  487.                 if (inParamPtr->error == noErr) {
  488.                     err = SPBRecord (inParamPtr, true);
  489.                     if (err != noErr)
  490.                         DebugStr("\pSPBRecord2 failed");
  491.                 }
  492.             }
  493.             myVarsPtr->theErr = err;
  494.         }
  495.     } else {
  496.         //There was an error from PBWrite, return the error in theErr of our structure
  497.         if (myVarsPtr->pb0->pb.ioParam.ioResult != noErr)
  498.             myVarsPtr->theErr = myVarsPtr->pb0->pb.ioParam.ioResult;
  499.         else myVarsPtr->theErr = myVarsPtr->pb1->pb.ioParam.ioResult;
  500.     }
  501.  
  502.     #if !GENERATINGCFM
  503.         oldA5 = SetA5 (oldA5);
  504.     #endif
  505.  
  506.     return;
  507. }
  508.  
  509. /*
  510.     These routines are here just for error checking in this example,
  511.     you may wish to update a status record or something similar.
  512.  
  513.     What do you do if you get a write error during async recording?
  514.     For this example we will simply terminate the recording process,
  515.     you should probably display an error.  Since the myParamBlockRec
  516.     doesn't contain the sound input device number we will check for
  517.     an error in the record completion routine and not start a new
  518.     recording if there was an error.  You can call SPBStopRecording
  519.     at interrupt time, but that isn't needed in this example.
  520.  
  521.     These routines display the error, but the record completion
  522.     routine checks the parameter blocks to make sure there was no
  523.     error before continuing the recording.
  524. */
  525. #if GENERATINGCFM
  526. pascal void MyPB0WriteComp (myParmBlkPtr passedPB)
  527. #else 
  528. pascal void MyPB0WriteComp (myParmBlkPtr passedPB:__a0)
  529. #endif
  530. {
  531.     if (passedPB->pb.ioParam.ioResult != noErr) {
  532.         DebugStr("\pPBWrite0 failed!");
  533.     }
  534.  
  535.     passedPB->pbInUse = false;
  536. }
  537.  
  538. #if GENERATINGCFM
  539. pascal void MyPB1WriteComp (myParmBlkPtr passedPB)
  540. #else 
  541. pascal void MyPB1WriteComp (myParmBlkPtr passedPB:__a0)
  542. #endif
  543. {
  544.     if (passedPB->pb.ioParam.ioResult != noErr) {
  545.         DebugStr("\pPBWrite1 failed!");
  546.     }
  547.  
  548.     passedPB->pbInUse = false;
  549. }
  550.  
  551.